package io.adrainty.bolee.security.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.adrainty.bolee.framework.commons.basic.ResponseCode;
import io.adrainty.bolee.framework.commons.exception.AppException;
import io.adrainty.bolee.framework.commons.user.DataSecurityEntity;
import io.adrainty.bolee.framework.commons.user.DeptPostUserEntity;
import io.adrainty.bolee.framework.commons.user.UserEntity;
import io.adrainty.bolee.security.models.bo.DeptPostUserBO;
import io.adrainty.bolee.security.models.bo.RoleDeptBO;
import io.adrainty.bolee.security.models.bo.UserRoleBO;
import io.adrainty.bolee.security.models.constants.UserConstants;
import io.adrainty.bolee.security.models.entity.RoleEntity;
import io.adrainty.bolee.security.models.properties.SecurityProperties;
import io.adrainty.bolee.security.models.valobj.UserTypeEnum;
import io.adrainty.bolee.security.repository.ICustomerRepository;
import io.adrainty.bolee.security.repository.IDeptRepository;
import io.adrainty.bolee.security.repository.IRoleRepository;
import io.adrainty.bolee.security.repository.IUserRepository;
import io.adrainty.bolee.security.service.IUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>UserServiceImpl</p>
 *
 * @author AdRainty
 * @version V1.0.0
 * @description IUserService
 * @since 2025/8/7 11:29:25
 */

@Slf4j
@Service
public class UserServiceImpl implements IUserService {

    @Resource
    private ICustomerRepository customerRepository;

    @Resource
    private IUserRepository userRepository;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private IDeptRepository deptRepository;

    @Resource
    private IRoleRepository roleRepository;

    @Resource
    private SecurityProperties securityProperties;

    @Override
    @Cacheable(value = UserConstants.LOGIN, key = "#userTypeEnum.code+'-'+#username+'-'+#company")
    public UserEntity findUserByUsername(UserTypeEnum userTypeEnum, String username, String company) {
        return switch (userTypeEnum) {
            case USER -> userRepository.findByUsername(username, company);
            case CUSTOMER -> customerRepository.findByUsername(username, company);
        };
    }

    @Override
    @Cacheable(value = UserConstants.LOGIN, key = "#userTypeEnum.code+'-'+#mobile+'-'+#company")
    public UserEntity findUserByMobile(UserTypeEnum userTypeEnum, String mobile, String company) {
        return switch (userTypeEnum) {
            case USER -> userRepository.findByMobile(mobile, company);
            case CUSTOMER -> customerRepository.findByMobile(mobile, company);
        };
    }

    @Override
    @Cacheable(value = UserConstants.LOGIN, key = "#userTypeEnum.code+'-'+#openId+'-'+#company")
    public UserEntity findUserByOpenId(UserTypeEnum userTypeEnum, String openId, String company) {
        return switch (userTypeEnum) {
            case USER -> userRepository.findByOpenId(openId, company);
            case CUSTOMER -> customerRepository.findByOpenId(openId, company);
        };
    }

    @Override
    @Caching(
            evict = {
                    @CacheEvict(value = UserConstants.PAGE, allEntries = true),
                    @CacheEvict(value = UserConstants.LIST, allEntries = true)
            },
            put = {@CachePut(value = UserConstants.BASIC, key = "#result.id")}
    )
    @Transactional
    public UserEntity createUser(UserEntity userEntity) {
        // 默认密码加密处理
        String defaultPwd = securityProperties.getPasswordDefault();
        defaultPwd = passwordEncoder.encode(defaultPwd);
        userEntity.setPassword(defaultPwd);

        boolean flag = userRepository.save(userEntity);
        if (!flag) {
            log.error("save user error");
            throw new AppException(ResponseCode.USER_CREATE_ERROR);
        }

        updateUserExtraData(userEntity);
        return userEntity;
    }

    @Override
    public IPage<UserEntity> findUserPage(UserEntity entity, Integer pageNum, Integer pageSize) {
        IPage<UserEntity> page = userRepository.findUserPage(entity, pageNum, pageSize);
        fillUserData(page.getRecords());
        return page;
    }

    @Override
    @Transactional
    public UserEntity updateUser(UserEntity entity) {
        if (entity.getId() == null) {
            throw new AppException(ResponseCode.ILLEGAL_PARAMETER);
        }

        if (!userRepository.updateUser(entity)) {
            throw new AppException(ResponseCode.USER_UPDATE_ERROR);
        }

        userRepository.deleteUserRoleByUserId(entity.getId());
        deptRepository.deleteUserDeptByUserId(entity.getId());
        updateUserExtraData(entity);
        return entity;
    }

    @Override
    public List<UserEntity> listUser(UserEntity entity) {
        List<UserEntity> result = userRepository.listUser(entity);
        fillUserData(result);
        return result;
    }

    @Override
    @Cacheable(value = UserConstants.DATA_SECURITY, key = "#userId")
    public DataSecurityEntity findUserDataSecurity(Long userId, List<RoleEntity> roleVOs) {
        if (CollUtil.isEmpty(roleVOs)) return null;
        boolean isYourselfData = roleVOs.stream()
                .anyMatch(r -> "0".equals(r.getDataScope()));
        if (isYourselfData) {
            return DataSecurityEntity.builder()
                    .yourselfData(Boolean.TRUE)
                    .deptNos(new ArrayList<>())
                    .build();
        }

        DataSecurityEntity securityEntity = new DataSecurityEntity();
        List<Long> roleIdList = roleVOs.stream()
                .filter(r -> "0".equals(r.getDataScope()))
                .map(RoleEntity::getId).toList();
        List<RoleDeptBO> roleDeptList = roleRepository.findRoleDeptByRoleIds(roleIdList);
        List<String> deptNos = roleDeptList.stream()
                .map(RoleDeptBO::getDeptNo).toList();
        securityEntity.setDeptNos(deptNos);
        return securityEntity;
    }


    /**
     * 更新用户扩展数据
     *
     * @param userEntity 用户实体
     */
    private void updateUserExtraData(UserEntity userEntity) {
        if (CollUtil.isNotEmpty(userEntity.getRoleVOIds())) {
            List<UserRoleBO> userRoleBOList = userEntity.getRoleVOIds().stream()
                    .map(r ->
                            UserRoleBO.builder()
                                    .roleId(Long.valueOf(r))
                                    .userId(userEntity.getId())
                                    .build()
                    ).toList();
            boolean roleResult = userRepository.saveBatchRole(userRoleBOList);
            if (!roleResult) {
                log.error("save user role batch error");
                throw new AppException(ResponseCode.USER_CREATE_ERROR);
            }
        }

        if (CollUtil.isNotEmpty(userEntity.getDeptPostUserVOs())) {
            List<DeptPostUserBO> deptPostUserList = userEntity.getDeptPostUserVOs()
                    .stream().map(d -> BeanUtil.copyProperties(d, DeptPostUserBO.class)).toList();
            boolean deptPostResult = deptRepository.saveBatchDeptPostUser(deptPostUserList);
            if (!deptPostResult) {
                log.error("save user dept post batch error");
                throw new AppException(ResponseCode.USER_CREATE_ERROR);
            }
        }
    }

    /**
     * 填充用户信息
     *
     * @param records 用户列表
     */
    private void fillUserData(List<UserEntity> records) {
        if (CollUtil.isEmpty(records)) return;
        List<Long> userIdList = records.stream().map(UserEntity::getId).toList();
        Map<Long, List<RoleEntity>> roleEntityMap = roleRepository.findRoleByUserId(userIdList).stream()
                .collect(Collectors.groupingBy(RoleEntity::getUserId));
        Map<Long, List<DeptPostUserBO>> deptEntityMap = deptRepository.findDeptPostUserListByUserId(userIdList).stream()
                .collect(Collectors.groupingBy(DeptPostUserBO::getUserId));
        records.forEach(item -> {
            List<RoleEntity> roleEntities = roleEntityMap.getOrDefault(item.getId(), new ArrayList<>());
            Set<String> roleIdSet = roleEntities.stream().map(RoleEntity::getId).map(String::valueOf).collect(Collectors.toSet());
            item.setRoleVOIds(roleIdSet);

            List<DeptPostUserBO> deptPostUserBOList = deptEntityMap.getOrDefault(item.getId(), new ArrayList<>());
            List<DeptPostUserEntity> deptPostUserEntities = BeanUtil.copyToList(deptPostUserBOList, DeptPostUserEntity.class);
            item.setDeptPostUserVOs(new HashSet<>(deptPostUserEntities));
        });
    }

}
